package tezhongbing;

import org.junit.Assert;
import org.junit.Test;

/**
 * 编译器优化一定是在编译阶段能确定优化后不会影响整体功能，类似于final引用，这个引用只能被赋值一次，但是它无法确定赋值的内容是什么。
 * 只有在编译阶段能确定这个final引用赋值的内容，编译器才有可能进行编译时优化，
 * 而在编译阶段能确定的内容只能来自于常量池中，例如int、long、String等常量，
 * 也就是不包含new String()、new Integer()这样的操作，因为这是运行时决定的，
 * 也不包含方法的返回值。因为运行时它可能返回不同的值，带着这个基本思想，对于编译时的优化理解就基本不会出错了。
 * @author sunniwell
 *
 */
public class StringTest {
	/**
	 * a虽然是由三个常量值+起来的，但它的值仍然是确定的、可预测的，所以虚拟机在编译阶段就进行了优化a=ab1
	 */
	@Test
	public void test1(){
		String a = "a"+"b"+1;
		String b = "ab1";
		Assert.assertTrue(a == b);
	}
	
	private String getA(){return "a";}
	
	/**
	 * 变量b是由变量a和常量"b"+起来的，虽然变量a指向的是个常量"a"，但它的引用还是可能发生改变，所以变量b的表达式不能被优化为一个常量。
	 * 变量c因为有了final修饰符，编译器就认为它是不可变的，所以d的值就是可预测的，因此会对d进行优化。
	 * 变量e是由一个函数和常量+起来的，编译器并不会去看函数内部做了什么，所以也不会被优化。
	 */
	@Test
	public void test2(){
		String a = "a";
		final String c = "a";
		String b = a+"b";
		String d = c+"b";
		String e = getA()+"b";
		String compare = "ab";
		Assert.assertFalse(b==compare);
		Assert.assertTrue(d==compare);
		Assert.assertFalse(e==compare);
	}
	
	/**
	 * 常量池在java用于保存在编译期已确定的，已编译的class文件中的一份数据。
	 * 它包括了关于类，方法，接口等中的常量，也包括字符串常量，是JVM的一块特殊的内存空间。
	 * 它里面的常量是全局唯一的。
	 * 当调用intern()方法时，JVM会在常量池中通过equals方法查找是否存在等值的String。
	 * 
	 */
	@Test
	public void test3(){
		String a = "a";
		String b = a+"b";
		String c = "ab";
		String d = new String(b);
		Assert.assertFalse(b==c);
		Assert.assertFalse(c==d);
		Assert.assertTrue(c==d.intern());
		Assert.assertTrue(b.intern()==d.intern());
	}
	
}
